#hMultiUserSelection User selection for MU-MIMO
#   SCHEDULE,PDSCHs =
#   hMultiUserSelection(CSI,TDDPATTERN,SPECIALSLOT,CARRIER,PDSCHs,BSANTSIZE,ALG)
#   performs user selection for MU-MIMO transmission, returning structure
#   SCHEDULE specifying the indices of PDSCH configurations to be
#   transmitted in this slot, and an array of PDSCH configurations, PDSCHs,
#   whose parameters have been updated for MU-MIMO transmissions in this
#   slot. CSI is a multi-user set of channel state information. TDDPATTERN
#   is the TDD UL-DL pattern. SPECIALSLOT is the special slot
#   configuration. CARRIER is the carrier configuration. PDSCHs is a
#   multi-user set of PDSCH configurations. BSANTSIZE is a vector
#   specifying the base station antenna array size. ALG is a structure
#   containing algorithmic options.

include("nrExtractResources.jl")
function hMultiUserSelection(csi, tddPattern, specialSlot, sensingSlotSymD, carrier, PDSCHs, bsAntSize, alg)

    # Determine the number of downlink OFDM symbols
    slotType = tddPattern[mod(carrier[:NSlot], length(tddPattern)) + 1]
    if slotType == "D"
        nSymbolsDL = carrier[:SymbolsPerSlot]
    elseif slotType == "S"
        nSymbolsDL = specialSlot[1]
    elseif slotType == "SC"
        nSymbolsDL = sensingSlotSymD
    else  # slotType == "U"
        nSymbolsDL = 0
    end

    # If there are no downlink symbols or no CSI has been recorded yet, no
    # PDSCH can be scheduled in this slot
    noCSI = all(isnan.(csi[1]["H"][:, :, 1, 1]))
    if nSymbolsDL == 0 || noCSI
        schedule = Dict("PDSCH" => zeros(1, 0))
        B = []
        return schedule, PDSCHs, B
    end

    # Determine the number of downlink antennas
    nTxAnts = prod(bsAntSize)

    # Replace NaNs in CSI with nearest non-NaN values, to provide CSI in
    # RBs outside of SRS bandwidth
    numUEs = length(PDSCHs)
    for ue in 1:numUEs
        H_2D = csi[ue]["H"][:, :, 1, 1]
        nanidx = findall(isnan.(H_2D))
        nonnanidx = findall(.!isnan.(H_2D))
        
        # 将 CartesianIndex{2} 转换为线性索引
        nanidx_linear = LinearIndices(H_2D)[nanidx]
        nonnanidx_linear = LinearIndices(H_2D)[nonnanidx]
        
        replaceidx = [nonnanidx_linear[argmin(abs.(nonnanidx_linear .- x))] for x in nanidx_linear]

        csi[ue]["nVar"][nanidx, :, :] = csi[ue]["nVar"][replaceidx, :, :]
        csi[ue]["H"][nanidx, :, :, :] = csi[ue]["H"][replaceidx, :, :, :]
    end

    # Extract number of layers from the PDSCH configurations and compute
    # total number of layers
    numLayers = [PDSCHs[ue][:Config][:NumLayers] for ue in 1:numUEs]
    totLayers = sum(numLayers)

    # Specify the number of RF units in the base station
    if !haskey(alg, :groups)
        groups = ones(numUEs)
    else
        groups = alg[:groups]
    end
    gmax = maximum(groups)

    isJSDMActive = alg[:PrecodingMethod] in ["JSDM-JGP", "JSDM-PGP"]
    if isJSDMActive
        numRF = alg[:ScheduledLayers]

        # Get Rg (group covariances)
        # Note that there is only one R per group (not per user). 
        # In this example, the group covariance is averaged over all
        # subcarriers, antennas, and users within the group. There is no
        # time averaging.
        numConnectedUEs = length(groups)

        # Get the channel estimates for each user in Nsc x Ntx x Nrx-ue
        hDp=Vector{Any}(undef,numConnectedUEs)
        for ue in 1:numConnectedUEs
        hDp[ue] = dropdims(csi[ue]["H"][:, 1, :, :], dims = (findall(size(csi[ue]["H"][:, 1, :, :]) .== 1)...,))
        end
        # Form the spatial covariance matrices for all groups
        prm = Dict("numGroups" => G, "groups" => groups)
        Rg = helperGetCovariance(hDp, prm)
    else
        numRF = nTxAnts
        B = []
    end

    # Determine how many streams go to each group
    G=Int(gmax)
    maxStreams = zeros(1, G)
    numStreamsPerGroup = ceil(numRF / G)
    maxStreams[1:G-1] .= numStreamsPerGroup
    maxStreams[G] = numRF - (G-1) * numStreamsPerGroup

    # Create variables used to indicate which PRGs are allocated to which
    # UEs, and to store the corresponding precoding matrices; note that
    # the PRG bundle size is assumed to be the same for all users
    prgInfo = nrPRGInfo(carrier, PDSCHs[1][:Extension][:PRGBundleSize])
    prgInfo_NPRG_Int=convert(Int,prgInfo[:NPRG])
    allUEs = fill(NaN,  prgInfo_NPRG_Int, alg[:ScheduledLayers])
    allW = zeros(Complex,prgInfo_NPRG_Int, alg[:ScheduledLayers], numRF)

    # Create vector of served throughput values for each UE and initialize
    # to a very small but non-zero value
    servedThroughput = 1e-6 * ones(numUEs)

    # For each PRG
    for PRG in 1:prgInfo[:NPRG]

        # Use channel and noise estimates from the central RB in the PRG
        index=findall(prgInfo[:PRGSet] .== PRG)
        RB = floor(sum(index) / length(index))
        # If the PRG is outside the carrier RBs, move to the next PRG
        if isnan(RB)
            continue
        end

        # Create channel estimates 'H' and noise estimates 'nVar' across
        # all layers, and create array 'ueLayers' which indicates which
        # layers of the overall estimates correspond to each UE
        H = zeros(Complex{Float64},totLayers, nTxAnts)
        nVar = zeros(Complex{Float64},totLayers, 1)
        layer = 1
        ueLayers = Vector{Any}(undef, numUEs)
        for ue in 1:numUEs
            ueLayers[ue] = layer
            for nu in 1:numLayers[ue]   
                H[layer, :] = csi[ue]["H"][Int(RB + carrier[:NStartGrid]), 1, :, nu]
                nVar[layer] = csi[ue]["nVar"][Int(RB + carrier[:NStartGrid]), 1, nu]
                layer += 1
            end
            ueLayers[ue]=collect(ueLayers[ue]:(layer-1))
        end

        # Compute power for each layer in the overall channel
        signalPowers = diag(H * H')

        # Compute total signal power, noise power, and SNR for each UE
        uePowers = [sum(signalPowers[ueLayers[ue]]) for ue in 1:numUEs]
        ueNoise = [sum(nVar[ueLayers[ue]]) for ue in 1:numUEs]
        ueSNR = Vector{Complex{Float64}}(undef, numUEs)
    
        for ue in 1:numUEs
                ueSNR[ue]=complex_divide(uePowers[ue],ueNoise[ue])
        end      

        # Calculate Proportional Fair (PF) metric based on instantaneous
        # single-user capacity and select UE with the highest metric - 
        # note that this metric is only used for fairness across the PRGs
        # in this slot, not across slots
        metricPF = capacity(ueSNR) ./ servedThroughput
        metricPF[findall(numLayers .> alg[:ScheduledLayers])] .= -Inf
        maxLayers = maxStreams .* ones(ceil(Int, numUEs / G), G)
        metricPF[[numLayers[i] .> maxLayers[i] for i in 1:numUEs]] .= -Inf

        maxPF=-Inf
        maxIdx=Int
        selectedLayers = Int[]
        selectedUEs=Int[]

        for ue in 1:numUEs
           uemetricPF=abs(metricPF[ue])
           if isnan( uemetricPF)
              continue
           else
              if uemetricPF>maxPF
                 maxPF=uemetricPF
                 maxIdx = Int[ue]
              end
            end
         end  

        if maxPF > -Inf
            selectedUEs = maxIdx
            selectedLayers =ueLayers[maxIdx][1]
        end

        # Calculate channel correlation matrix
        scaledH = normalizeMatrix(H)
        R = abs.(scaledH * scaledH')

        # While not all UEs have been selected
        while length(selectedUEs) < numUEs

            # Calculate correlation between all UEs and the UEs selected
            # already
            if length(selectedUEs)>0
                corrLayers = sum(R[selectedLayers, :], dims=1)
            else
                corrLayers = zeros( totLayers )
            end
            corrUEs = [sum(corrLayers[ueLayers[ue]]) for ue in 1:numUEs]

            # Select a further UE subject to the following conditions:
            # The UE has not been selected already
            corrUEs[selectedUEs] .= Inf
            # The number of layers for the UE fits within the target
            # number of scheduled layers
            corrUEs[(length(selectedLayers) .+ numLayers) .> alg[:ScheduledLayers]] .= Inf
            # The group capacity has not been exceeded
            groupCapacity = copy(maxStreams)
            for u in selectedUEs
                groupCapacity[Int(groups[u])] -= length(ueLayers[u])
            end
            for g in 1:G
                corrUEs[(groups .== g) .& (groupCapacity[g] .- numLayers .< 0)] .= Inf
            end
            # The UE has the lowest correlation
            minCorrelation, minIdx = findmin(corrUEs)

            if isfinite(minCorrelation)
                # Select the UE meeting the conditions above
                push!(selectedUEs, minIdx)
                selectedLayers=[selectedLayers...; ueLayers[minIdx]...]
            
            else
                # No UEs met the conditions above, select no further UEs
                break
            end
        end

        # For JSDM, the selectedUEs and selectedLayers need to be reordered
        # so they fit within groups, rather than ordered by highest SNR
        # to lowest SNR. 
        if isJSDMActive
            tempSelectedUEs = Int[]
            tempSelectedLayers = Int[]
            for g in 1:G
                append!(tempSelectedUEs, selectedUEs[groups[selectedUEs] .== g])
            end
            for u in tempSelectedUEs
                append!(tempSelectedLayers, ueLayers[u])
            end
            selectedUEs = tempSelectedUEs
            selectedLayers = tempSelectedLayers
        end

        # Extract the channels for each selected UE, 'ueH', and create the
        # overall channel across all selected UEs, 'selectedH'. Also record
        # which selected layer corresponds to which UE, 'ownerUEs'
        numSelectedUEs = length(selectedUEs)
        selectedNumLayers = numLayers[selectedUEs]
        ownerUEs = zeros(Int, sum(selectedNumLayers))
        ueH=Vector{Any}(undef,numSelectedUEs)
        layer = 1
        for ue in 1:numSelectedUEs
            ueH[ue]=collect(transpose(H[ueLayers[selectedUEs[ue]], :]))
            nu = selectedNumLayers[ue]
            ownerUEs[layer:layer+nu-1] .= selectedUEs[ue]
            layer += nu
        end

        selectedH = collect(transpose(hcat(ueH...)))
        # Calculate precoding weights using the selected precoding method
        if !isempty(selectedNumLayers)
            if alg[:PrecodingMethod] in ["JSDM-JGP", "JSDM-PGP"]
                layerSNR = [complex_divide(signalPowers[i],nVar[i]) for i in selectedLayers]
                B, W, selectedH = jsdm(ueH, Rg, layerSNR, numLayers, selectedUEs, alg, maxStreams)
            elseif alg[:PrecodingMethod] == "BD"
                W = blkdiagbfweights(ueH, selectedNumLayers)
            elseif alg[:PrecodingMethod] in ["RZF", "ZF"]
                layerSNR = [complex_divide(signalPowers[i],nVar[i]) for i in selectedLayers]
                W = rzf(selectedH, layerSNR, alg)
            end
        else
            W = []
        end

        # Calculate the capacity per layer and per UE
        SINR = computeSINR(selectedH, W, nVar[selectedLayers])
        layerCapacity = capacity(SINR)
        l=0
        ueCapacity=[];
        for ue in 1:numSelectedUEs
            l+=selectedNumLayers[ue]
            push!(ueCapacity, sum(layerCapacity[ 1+l-selectedNumLayers[ue]:l]))
        end
        # Record the served throughput for the selected UEs, on the
        # assumption that the transmission would be successful
        servedThroughput[selectedUEs] .+= ueCapacity

        # Record which PRGs are allocated to which UEs, and store the
        # corresponding precoding matrices
        n = length(ownerUEs)
        intPRG=Int(PRG)
        allUEs[intPRG, 1:n] = ownerUEs
        allW[intPRG, 1:n, :] .= W
 
    end

    # Sort the UEs into vector 'u' which lists them in descending order of
    # the number of overlapping PRGs
    A = zeros(numUEs, numUEs)
    for PRG in 1:prgInfo[:NPRG]
        thisPRG = allUEs[Int(PRG), :]
        thisPRG = thisPRG[.!isnan.(thisPRG)]
        r = Vector{Int64}(unique(thisPRG))
        A[r, r] .+= 1
    end
 
    # Extract the upper triangular part of the matrix
    U = zeros(numUEs, numUEs)
    for i in 1:numUEs
        for j in i:numUEs
            U[i, j] = A[i, j]
        end
    end

    u = Int[]
    while length(u) < numUEs && sum(U) > 0
        value, index = findmax(U)
        i, j = index[1], index[2]
        u=unique!(push!(u, i, j))
        U[i, j] = 0
    end

    # Assign ports and scrambling identities to the UEs in the order 'u'
    # determined above. Orthogonal DM-RS ports are first assigned to the
    # UEs with the most overlapping PRGs, ensuring that the set of DM-RS
    # ports is valid, that is, the set can be signaled by a DCI message
    # (see TS 38.212 Table 7.3.1.2.2-2). When the orthogonal ports have all
    # been used, nonorthogonal DM-RS are configured by reusing the ports
    # and assigning different scrambling identities
    c = 0
    p = [0, 1, 4, 5, 2, 3, 6, 7]
    ports = Vector{Any}(undef, numUEs)
    ID = fill(NaN, 1,numUEs)

    # For each scheduled UE
    for i in 1:length(u)

        # Get the number of layers 'nu' for that UE
        ue = u[i]
        nu = numLayers[ue]

        # Update index 'c' to the next position in vector of ports 'p' that
        # gives 'nu' ports that can be signaled by a DCI message, and
        # allocate those ports to the current UE
        c += mod(-c, nu)
        ports[ue] = p[mod.(c .+ (0:nu-1), length(p)) .+ 1]

        # When 'c' is greater than or equal to the length of 'p', a set of
        # orthogonal ports is being reused, so assign a different
        # scrambling identity than that assigned to other UEs using those
        # same ports
        ID[ue] = floor(Int, c / length(p))

        # Update index 'c' to the position beyond the ports used for
        # current UE
        c += nu
    end

    # Establish which UEs are scheduled
    schedule = Dict("PDSCH" => sort(unique(allUEs[.!isnan.(allUEs)])))


# Note: NaN in Julia is represented using the `NaN` function from the `Base` module.
    # For each scheduled UE
    for ue in schedule["PDSCH"]
        ue=Int(ue)
        # Configure the symbol allocation according to the TDD
        # configuration
        if slotType == "SC"
            PDSCHs[ue][:Config][:SymbolAllocation] = [carrier[:SymbolsPerSlot] - nSymbolsDL, nSymbolsDL]
        else
            PDSCHs[ue][:Config][:SymbolAllocation] = [0, nSymbolsDL]
        end

        # Configure the set of PRBs allocated to this UE
        i = findall(allUEs .== ue)
        prg=[ i[row][1] for row in 1:length(i) ]
        lndx = LinearIndices(allUEs)[i]
        ueW,~ = nrExtractResources(lndx, allW)
 
        prg, iprb = sort(prg), sortperm(prg)
        prg = unique(prg)
        PDSCHs[ue][:Config][:PRBSet] = findall(prgInfo[:PRGSet] .∈ [prg]) .- 1

        # Configure precoding matrices
        nu = numLayers[ue]
        PDSCHs[ue][:Config][:NumLayers] = nu
        ueW = permutedims(reshape(ueW[iprb, :], nu, :, numRF), [1, 3, 2])
        PDSCHs[ue][:Extension][:W] = zeros(Complex,nu, numRF, Int(prgInfo[:NPRG]))
        PDSCHs[ue][:Extension][:W][:, :, prg] = ueW

        # Configure DM-RS port set
        PDSCHs[ue][:Config][:DMRS][:DMRSPortSet] = ports[ue]

        # Configure scrambling identities if required
        if ID[ue] != 0
            PDSCHs[ue][:Config][:DMRS][:NIDNSCID] = ID[ue]
            PDSCHs[ue][:Config][:DMRS][:NSCID] = 1
        end

        if slotType == "SC"
            PDSCHs[ue][:Config][:DMRS][:CustomSymbolSet] = (carrier[:SymbolsPerSlot] - nSymbolsDL):(carrier[:SymbolsPerSlot] - nSymbolsDL + 1)
        else
            PDSCHs[ue][:Config][:DMRS][:CustomSymbolSet] = []
        end
    end

    return schedule, PDSCHs, B
end

# Perform ZF or RZF precoding
# For more information see Peel, C.B., et al. "A Vector-Perturbation 
# Technique for Near-Capacity Multiantenna Multiuser Communication—Part I:
# Channel Inversion and Regularization." IEEE Transactions on Signal 
# Processing, Vol. 53, No. 1, February 2005, pp. 195-202
function rzf(H, SNR, alg)
    numUEs = size(H, 1)

    if alg[:PrecodingMethod] == "ZF"
        W = H' / (H * H')
    elseif alg[:PrecodingMethod] == "RZF"
        I=zeros(numUEs,numUEs)
        for i in 1:numUEs
            I[i,i]=1
        end
        W = H' / (H * H' + (1 ./ SNR) .* I)
    end
    W=collect(transpose(W))
    W = normalizeMatrix(W)
    return W
end

# Perform JSDM hybrid precoding
# For more information see Adhikary, A., et al. "Joint Spatial Division and
# Multiplexing - The Large-Scale Array Regime." IEEE Transactions on
# Information Theory, Vol. 59, No. 10, October 2013, pp. 6441-6463.
function jsdm(ueH, Rg, SNR, numLayers, selectedUEs, alg, maxStreams)
    groups = alg["groups"]
    G = maximum(groups)
    selectedNumLayers = numLayers[selectedUEs]
    numSelectedUEs = length(selectedUEs)

    # Determine the layers in each group
    numLayersInGroup = zeros(1,G)
    for g in 1:G
        numLayersInGroup[g] = sum(numLayers[intersect(selectedUEs, findall(x -> x == g, groups))])
    end

    # Calculate the analog beamformer weights (Nsg x Ntx)
    Bg = jsdmrfweights(Rg, maxStreams)

    # Construct RF beamformer matrix to align with the assigned streams for the receiving UEs
    B = vcat(Bg...)

    # Calculate the effective channel response for each UE
    Heff = Vector{Any}(undef, numSelectedUEs)
    for ue in 1:numSelectedUEs
        Heff[ue] = B * ueH[ue]
    end
    selectedH = hcat(Heff...)

    meanNoise = 1 / sum(SNR)

    if alg["PrecodingMethod"] == "JSDM-JGP"
        # Compute precoding weights based on joint group processing (JGP)
        W = jsdmbbweights(selectedH', numLayersInGroup, Bg, "RegularizationFactor" => meanNoise)
    else
        # Compute precoding weights based on per-group processing (PGP)
        # using less CSI feedback

        # Calculate the effective channel for each group
        hEffG = Vector{Any}(undef, G)
        for n in 1:numSelectedUEs
            idx = groups[selectedUEs[n]]
            hEffG[idx] = hcat(hEffG[idx], Heff[n])
        end

        # Partition out group H matrices for PGP
        hEffGG = Vector{Any}(undef, G)
        for g in 1:G
            hEffGG[g] = hEffG[g][sum(maxStreams[1:g-1]) + 1 : sum(maxStreams[1:g])]
        end
        
        Wg = jsdmbbweights(hEffGG, numLayersInGroup, Bg, "RegularizationFactor" => meanNoise)

        # Construct W (block diagonal) from Wg
        W = zeros(sum(selectedNumLayers), sum(maxStreams))
        n = 1
        for g in 1:G
            wIndex = sum(maxStreams[1:g-1]) + 1 : sum(maxStreams[1:g])
            W[n:n + size(Wg[g], 1) - 1, wIndex] .= Wg[g]
            n += size(Wg[g], 1)
        end
    end

    return B, W, selectedH
end

# Normalize rows of matrix A such that diagonal elements of A*A' are 1
function normalizeMatrix(A)
    return (1 ./ sqrt.(diag(A*A'))) .* A;
end

# Compute SINR of effective channel H*W, given channel matrix H, precoding
# matrix W and noise variance nVar
function computeSINR(H,W,nVar)

    W = collect(transpose(W))
    D = H*W
    S = abs.(diag(D)).^2
    I = real.(diag(D*D') - S)
    SINR = [complex_divide(S[i],I[i] + nVar[i]) for i in 1:size(I)[1]]
    return SINR
end

# Calculate capacity according to Shannon–Hartley theorem. The bandwidth B
# is not considered because it is the same in all contexts where the
# capacity is compared
function capacity(SINR)
    return log2.(1 .+ SINR)
end

# Presents the diagonal elements of the matrix
function diag(matr)
    return [matr[i,i] for i in 1:minimum(size(matr))]
end

# 处理分母是0+0im时候的除法
function complex_divide(z1, z2::Complex{T}) where T
    if real(z2) == 0 && imag(z2) == 0
        if real(z1) != 0 && imag(z1) == 0
            return Inf + 0im  
        elseif real(z1) == 0 && imag(z1) != 0
            return 0 + Inf*im  
        elseif real(z1) != 0 && imag(z1) != 0
            return Inf + Inf*im  
        end
    end
    return z1 / z2
end

function jsdmrfweights(Rg, Sg, bg=nothing)
    #using LinearAlgebra
    # G: 用户组数
    G = length(Rg)
    Nt = size(Rg[1])[1]  # 获取天线数量
    
    # 初始化变量
    rg = zeros(Int, G)
    Ugs = cell(G)
    
    # 计算每个组的信道矩阵的秩
    for g in 1:G
        Rgcurrent = Rg[g]
        rg[g] = rank(Rgcurrent)  # 计算矩阵秩
    end
    
    # 如果没有提供 NTRF (RF链数量)，假定 NTRF 和 NS 相同
    if isnothing(bg)
        bg = Sg
    end

    # 检查总的 RF 链数量不超过天线数量
    total_rf = sum(bg)
    if total_rf > Nt
        error("Total number of RF chains exceeds the number of antennas.")
    end
    
    # 计算 Ugs (空间信道矩阵的奇异值分解)
    for g in 1:G
        Rgcurrent = Rg[g]
        U, _, _ = svd(Rgcurrent)
        Ugs[g] = U[:, 1:rg[g]]  # 取前 rg(g) 个奇异向量
    end
    
    Bg = cell(G)  # 存储最终的 RF 预编码权重

    # 计算每个组的 RF 预编码权重
    for g in 1:G
        if bg[g] == rg[g]
            # 使用 SVD 计算，并直接转置得到权重
            Bg[g] = Ugs[g]'
        else
            # 使用块对角化 (BD)
            X = hcat([Ugs[i] for i in setdiff(1:G, g)])  # 合并所有其他组的 Ugs
            _, _, V = svd(X)  # 对合并后的矩阵进行 SVD
            Eg0 = V[:, rg[g]+1:end]  # 获取 Eg0
            Rghat = Eg0' * Rg[g] * Eg0  # 计算 Rghat
            _, _, Gg = svd(Rghat)  # 计算 Gg
            Bg[g] = (Eg0 * Gg[:, 1:bg[g]])'  # 计算 RF 权重并转置
        end
    end
    
    return Bg
end

function nrPRGInfo(carrier, prgsize)
    NRB=Int(carrier[:NSizeGrid])
    nstartgrid=Int(carrier[:NStartGrid])
    # Calculate the number of carrier resource blocks (CRB)
    NCRB = nstartgrid +  NRB
    
    # Handle the case of empty PRG size, which configures a single fullband PRG
    if isempty(prgsize)
        Pd_BWP = NCRB
    else
        Pd_BWP = prgsize
    end

    # Calculate the number of precoding resource block groups
    NPRG = ceil(NCRB / Pd_BWP)

    # Calculate the 1-based PRG indices for each RB in the carrier grid
    Pd_BWP = ceil(NCRB / NPRG)
    prgset = repeat(collect(1:NPRG), inner=Int(Pd_BWP))
    prgset = prgset[ (1+nstartgrid): NCRB ]

    # Return the PRG info
    return Dict(:NPRG => NPRG, :PRGSet => prgset)
end
########################################################################################################################
sensingSlotSymD=7
specialSlot=[10,2,2]
tddPattern = ["SC" "D" "D" "S" "U" "SC" "D" "D" "S" "U"]
bsAntSize = [16 8 2]
algParameters=Dict{Any,Any}(:PrecodingMethod => "RZF", :ScheduledLayers => 8, :PerfectChannelEstimator => 1)
numLayers = [1,4,4,2,4]
ueAntSizes = 1 .+ (numLayers .> [4 2 1])
carrier=Dict{Any, Any}(
    :NCellID => 1,
    :SubcarrierSpacing => 30,
    :CyclicPrefix => "normal",
    :NSizeGrid => 51,
    :NStartGrid => 0,
    :NSlot => 5,
    :NFrame => 0,
    :IntraCellGuardBands => [0.0 0.0],

    :SymbolsPerSlot => 14,
    :SlotsPerSubframe => 2,
    :SlotsPerFrame => 20
)

using MAT
function setupCSI(carrier, bsAntSize, ueAntSizes)
    # Set up record of CSI obtained via SRS
    
    NCRB = carrier[:NSizeGrid] + carrier[:NStartGrid]
    numUEs = size(ueAntSizes, 1)
    
    # 创建字典数组
    csi = Vector{Dict{String, Any}}(undef, numUEs)
    P = prod(bsAntSize)
    
    for ue in 1:numUEs
        R = prod(ueAntSizes[ue, :])
        
        # 初始化每个CSI实例的字段
        csi[ue] = Dict{Any, Any}(
            "H" => fill(NaN,  NCRB, 1, P, R),
            "nVar" => zeros(Int, NCRB, 1, R),           # nVar 字段
            "nVarPUSCH" => zeros(Int, NCRB, 1, R),      # nVarPUSCH 字段
            "NSlot" => fill(NaN,  NCRB, 1)    # NSlot 字段
        )
    end
    
    return csi
end
csi = setupCSI(carrier, bsAntSize, ueAntSizes)
data_file = matread("code\\hMultiUserSelectionData.mat")
csii=data_file["csi"]
csi[1]["H"]=csii["H"][1]
csi[2]["H"]=csii["H"][2]
csi[3]["H"]=csii["H"][3]
csi[4]["H"]=csii["H"][4]
csi[5]["H"]=csii["H"][5]
csi[1]["NSlot"]=csii["NSlot"][1]
csi[2]["NSlot"]=csii["NSlot"][1]
csi[3]["NSlot"]=csii["NSlot"][1]
csi[4]["NSlot"]=csii["NSlot"][1]
csi[5]["NSlot"]=csii["NSlot"][1]
csi[1]["nVarPUSCH"]=csii["nVarPUSCH"][1]
csi[2]["nVarPUSCH"]=csii["nVarPUSCH"][2]
csi[3]["nVarPUSCH"]=csii["nVarPUSCH"][3]
csi[4]["nVarPUSCH"]=csii["nVarPUSCH"][4]
csi[5]["nVarPUSCH"]=csii["nVarPUSCH"][5]

PDSCHs = Dict{Any, Dict}()
for i in 1:5
    PDSCHs[i] = Dict(
        :Config => Dict(
            :ReservedRE => [],
            :NID => [],
            :NSizeBWP => [],
            :NStartBWP => [],
            :Modulation => "64QAM",
            :ReservedPRB => Dict(:PRBSet => [], :SymbolSet => [], :Period => []),
            :PRBSetType => "VRB",
            :VRBToPRBInterleaving => 0,
            :VRBBundleSize => 2,
            :DMRS => Dict(
                :DMRSConfigurationType => 1,
                :DMRSReferencePoint => "CRB0",
                :NumCDMGroughWithoutData => 2,
                :DMRSDownlinkR16 => 0,
                :DMRSTypeAPosition => 2,
                :DMRSAdditionalPosition => 0,
                :DMRSLength => 2,
                :CustomSymbolSet => [],
                :DMRSPortSet => [],
                :NIDNSCID => [],
                :NSCID => 0,
                :DMRSEnhancedR18 => 0,
                :CDMGroughs => 0,
                :DeltaShifts => 0,
                :FrequencyWeights => [1;1],
                :TimeWeights => [1;1],
                :DMRSSubcarrierLocations => [0;2;4;6;8;10],
                :CDMLengths => [1;1]
            ),
            :PTRS => Dict(
                :PTRSPortSet => [],
                :TimeDensity => 1,
                :FrequencyDensity => 2,
                :REOffset => "00"
            ),
            :NumLayers => 1,
            :MappingType => "A",
            :SymbolAllocation => [0, 14],
            :PRBSet => collect(0:51),
            :RNTI => 1,
            :EnablePTRS => 0,
            :NumCodewords => 1
        ),
        :Extension => Dict(
            :W => Array{Complex}[],
            :PRGBundleSize => 2,
            :TargetCodeRate => 0.7539,
            :XOverhead => 0
        )
    )
    PDSCHs[i][:Config][:NumLayers]=numLayers[i]
end

schedule,PDSCHs,B=hMultiUserSelection(csi, tddPattern, specialSlot, sensingSlotSymD, carrier, PDSCHs, bsAntSize, algParameters)
